package dyyx.httpclient;

import java.net.URI;
import java.time.LocalTime;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;


public class SyncHttpClient2 {
    public static void main(String[] args) throws Exception {
    	String url = "http://codefun007.xyz/pv.htm";
        HttpClient client = new HttpClient(url);
        client.connect();
        int num = 3;
        for(int i=0;i<num;i++){
        	// 返回结果是否有可能乱序
        	// 客户端接收报文可能不完整 
            System.out.println(client.getBody());
        }
        
        long start = System.currentTimeMillis();
        client.close();
        long end = System.currentTimeMillis();
        long time = end - start;
        System.out.println("close time "+time);
        
        
        
    }

    public static class HttpClient {
        private ClientHandler clientHandler = new ClientHandler();
        private String url;
        private URI uri;
        private volatile Channel channel;
        volatile Bootstrap bootstrap = null;


        public HttpClient(String url) {
            this.url = url;
        }

        public void connect() throws Exception {
            uri = new URI(url);
            EventLoopGroup loopGroup = new NioEventLoopGroup();
            Bootstrap b = new Bootstrap();
            bootstrap = b;
            b.group(loopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline()
                    // .addLast(new HttpRequestEncoder())
                    //  .addLast(new HttpResponseDecoder())
                   
                    .addLast(new HttpClientCodec())
                    // HTTP 消息的合并处理 
                    // 不加这个会卡住 that reached at the tail of the pipeline. Please check your pipeline configuration.
                    .addLast(new HttpObjectAggregator(2048))
                   
                    .addLast(clientHandler);
                }
            });
            ChannelFuture channelFuture = b.connect(uri.getHost(), uri.getPort() < 0 ? 80 : uri.getPort()).sync();
            long start = System.currentTimeMillis();
            channelFuture.get();
            long end = System.currentTimeMillis();
            long time = end - start;
            System.out.println("channelFuture.get.time "+time);
            Channel channel = channelFuture.channel();
            
            this.channel = channel;

            
            while (true) {
            	System.out.println(LocalTime.now()+","+getChannelInfo(channel));
            	if(channel.isActive()){
            		break;
            	}
                Thread.sleep(10);
            }
            
        }
        
        final String getChannelInfo(Channel channel){
        	StringBuilder sb = new StringBuilder();
        	sb.append("active="+channel.isActive()).append(",");
        	sb.append("open="+channel.isOpen()+",");
        	sb.append("writable="+channel.isWritable()+",");
        	sb.append("isRegistered="+channel.isRegistered());

        	return sb.toString();
        }
        
        public void close()throws Exception{
        	if(channel==null){
        		return;
        	}
        	channel.close().sync().get();
        	// 优雅关闭 才能正常退出
        	bootstrap.group().shutdownGracefully();
        }

        public String getBody() throws Exception {
            DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString());
            request.headers().set(HttpHeaders.Names.HOST, uri.getHost());
            request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
            request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, request.content().readableBytes());
            ChannelPromise promise = clientHandler.sendMessage(request);
            promise.await();
            return clientHandler.getData();
        }
    }
    //  注意这里是 FullHttpResponse  ，服务端是 FullHttpRequest
    public static class ClientHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
        private volatile ChannelHandlerContext ctx;
        private volatile ChannelPromise promise;
        private volatile String data;

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            super.channelActive(ctx);
            this.ctx = ctx;
        }

        public ChannelPromise sendMessage(Object message) {
            if (ctx == null)
                throw new IllegalStateException();
            promise = ctx.writeAndFlush(message).channel().newPromise();
            return promise;
        }

        public String getData() {
            return data;
        }

        @Override
    	public void channelRead0(ChannelHandlerContext ctx, FullHttpResponse res) {
        	ByteBuf buf = res.content();   
    		String content = buf.toString(CharsetUtil.UTF_8);   		
    		this.data = content;
    		promise.setSuccess();  
        }
    }

}